import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.awt.event.*;
import java.io.*;

class ListModel<T> extends AbstractListModel implements Iterable {
   private java.util.List<T> rules=new LinkedList<T>();

   @Override public Object getElementAt(int index) {
      return rules.get(index);
   }
   @Override public int getSize() {
      return rules.size();
   }
   public void add(T r) {
      rules.add(r);
      fireIntervalAdded(this, rules.size()-1, rules.size()-1);
   }
   public T get(int index) {
      return rules.get(index);
   }
   public void remove(T o) {
      int index=rules.indexOf(o);
      rules.remove(o);
      fireIntervalRemoved(this, index, index);
   }
   public void clear() {
      if(rules.isEmpty()) return;
      fireIntervalRemoved(this, 0, rules.size()-1);
      rules.clear();
   }
   public boolean contains(T o) {
      return rules.contains(o);
   }
   public Iterator<T> iterator() {
      return rules.iterator();
   }
}
class RuleManager extends JDialog {
   private ListModel<Rule> rules=new ListModel<Rule>();
   public static final String RULE_FILE_NAME="rules";
   final WorldFrame worldFrame;
   final JList ruleList=new JList();

   RuleManager(WorldFrame parent) {
      super(parent);
      worldFrame=parent;

      setTitle("Rule Manager");
      //setSize(350, 280);
      //setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
      setLayout(new BorderLayout());
      final JLabel msgLabel=new JLabel("Select and manage rules.");
      msgLabel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
      add(msgLabel, BorderLayout.PAGE_START);

      final JPanel listPanel=new JPanel();
      listPanel.setLayout(new BoxLayout(listPanel, BoxLayout.LINE_AXIS));
      //listPanel.setLayout(new BorderLayout());
      //listPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
      ruleList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      //ruleList.setPreferredSize(new Dimension(200, 200));
      ruleList.setModel(rules);

      listPanel.add(Box.createRigidArea(new Dimension(5,0)));
      final JScrollPane ruleScroll=new JScrollPane(ruleList);
      ruleScroll.setPreferredSize(new Dimension(200, 150));
      listPanel.add(ruleScroll);
      listPanel.add(Box.createRigidArea(new Dimension(5,0)));

      final JPanel buttonPanel=new JPanel();
      buttonPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
      buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.PAGE_AXIS));
      final JButton newButton=new JButton("New");
      newButton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            final String ruleStr=JOptionPane.showInputDialog(RuleManager.this, "Please input a rule, ex. \"123/456/5\".");
            if(ruleStr==null) return;
            if(!Rule2D.isValidStr(ruleStr)) {
               JOptionPane.showMessageDialog(RuleManager.this, "Invalid Rule Format!");
               return;
            }
            //Rule r=new Rule2D(sStr, bStr, Integer.parseInt(gStr));
            final Rule r=new Rule2D(ruleStr);
            if(rules.contains(r)) {
               JOptionPane.showMessageDialog(RuleManager.this, String.format("Rule %s Already Exists!", r));
            } else {
               rules.add(r);
               ruleList.setSelectedValue(r, true);
            }
            //System.out.println(r);
         }
      });
      buttonPanel.add(newButton);
      //buttonPanel.add(Box.createRigidArea(new Dimension(0,5)));
      //buttonPanel.add(new JButton("Edit"));
      buttonPanel.add(Box.createRigidArea(new Dimension(0,5)));

      final JButton deleteButton=new JButton("Delete");
      deleteButton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            int lastIndex=ruleList.getSelectedIndex();
            rules.remove((Rule)ruleList.getSelectedValue());
            ruleList.setSelectedIndex(lastIndex%rules.getSize());
         }
      });
      buttonPanel.add(deleteButton);
      buttonPanel.add(Box.createVerticalGlue());

      listPanel.add(buttonPanel, BorderLayout.LINE_END);

      add(listPanel);
      
      

      final JPanel closePanel=new JPanel();
      closePanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
      final JButton okButton=new JButton("OK");
      okButton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            try {
               //select and save
               Rule r=(Rule)ruleList.getSelectedValue();
               worldFrame.getWorld().setRule(r);
               worldFrame.setTitle(worldFrame.getWorld().toString());
               toFile(new File(RULE_FILE_NAME));
               setVisible(false);
            } catch(IOException xpt) {
               xpt.printStackTrace();
            }
         }
      });
      closePanel.add(okButton);

      final JButton cancelButton=new JButton("Cancel");
      cancelButton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            setVisible(false);
         }
      });
      closePanel.add(cancelButton);
      //closePanel.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
      add(closePanel, BorderLayout.PAGE_END);
      pack();
   }
   private void loadData() {
      try {
         File f=new File(RULE_FILE_NAME);
         if(!f.exists()) f.createNewFile();
         fromFile(f);
      } catch(IOException e) {
         e.printStackTrace();
      }
      ruleList.setSelectedValue(worldFrame.getWorld().getRule(), true);
   }
   @Override public void setVisible(boolean v) {
      if(v) {
         setLocation(getParent().getX()+(getParent().getWidth()-getWidth())/2, 
                     getParent().getY()+(getParent().getHeight()-getHeight())/2);
         loadData();
      }
      super.setVisible(v);
   }
   /*
   public static void main(String[] args) {
      JFrame jf=new JFrame();
      RuleManager ruleMgr=new RuleManager(jf);
      ruleMgr.setVisible(true);
   }
   */
   public synchronized String toText() {
      StringBuffer sb=new StringBuffer();
      // for(Rule r: rules) //>>> can't compile???
      for(Iterator<Rule> i=rules.iterator(); i.hasNext(); ) {
         sb.append(i.next().toString()).append("\n");
      }
      return sb.toString();
   }
   public synchronized void fromText(String src) {
      rules.clear();
      for(int index=0; index<src.length(); ) {
         int newLine=src.indexOf("\n", index);
         String ruleStr=
            src.substring(index, newLine);
         rules.add(new Rule2D(ruleStr));
         index=newLine+1;
      }
   }
   public void fromFile(File file) throws IOException { //>>> duplicate with World2D
      StringBuilder sb=new StringBuilder();
      FileReader reader=null;
      try {
         reader=new FileReader(file);
         int c;
         while((c=reader.read())!=-1) {
            sb.append((char)c);
         }
      } catch(IOException e) {
         e.printStackTrace();
      } finally {
         if(reader!=null) reader.close();
      }
      if(sb.length()==0) return; 
      fromText(sb.toString());
   }
   public void toFile(File file) throws IOException { //>>> duplicate with World2D
      FileWriter writer=null;
      try {
         writer=new FileWriter(file);
         writer.write(toText());
      } catch(IOException e) {
         e.printStackTrace();
      } finally {
         if(writer!=null) writer.close();
      }
   }
}
